package com.leslie.wukong.biz.system.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leslie.wukong.biz.product.converter.ObjectConverter;
import com.leslie.wukong.biz.system.domain.SysUser;
import com.leslie.wukong.biz.system.domain.dto.SysUserAddDTO;
import com.leslie.wukong.biz.system.domain.dto.UserLoginDTO;
import com.leslie.wukong.biz.system.domain.vo.UserLoginVO;
import com.leslie.wukong.biz.system.mapper.SysUserMapper;
import com.leslie.wukong.biz.system.service.SysUserService;
import com.leslie.wukong.common.core.constants.CacheConstants;
import com.leslie.wukong.common.core.constants.Constants;
import com.leslie.wukong.common.core.constants.SecurityConstants;
import com.leslie.wukong.common.core.constants.UserConstants;
import com.leslie.wukong.common.core.exception.ServiceException;
import com.leslie.wukong.common.core.properties.JwtProperties;
import com.leslie.wukong.common.core.utils.JwtUtil;
import com.leslie.wukong.common.core.uuid.IdUtils;
import com.leslie.wukong.common.redis.service.RedisService;
import com.leslie.wukong.common.security.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * ClassName: SysUserServiceImpl
 * Package: com.leslie.wukong.biz.system.service.impl
 * Description:
 * @date: 2024/12/4 14:12
 * @author: itcoder
 */

@Service
@Slf4j
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    private final SysUserMapper mapper;
    private final RedisService redisService;
    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private final static long expireTime = CacheConstants.EXPIRATION;

    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;

    private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
    private final JwtProperties jwtProperties;

    @Override
    public int insertSelective(SysUser record) {
        return baseMapper.insertSelective(record);
    }

    @Override
    public int updateByPrimaryKeySelective(SysUser record) {
        return baseMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public Integer add(SysUserAddDTO dto) {
        String phoneNumber = dto.getPhoneNumber();
        // 校验账号是否存在
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUserName, dto.getUserName())
                .eq(SysUser::getDelFlag, Constants.NOT_DEL);
        SysUser sysUserDb = mapper.selectOne(wrapper);
        if (ObjectUtil.isNotNull(sysUserDb)) {
            throw new ServiceException("用户名已存在");
        }
        // 校验手机号
        if (StrUtil.isNotBlank(phoneNumber)) {
            if (!Validator.isMobile(phoneNumber)) {
                throw new InvalidParameterException("手机号码格式不正确");
            }
        }
        // 密码加密
        SysUser sysUser = ObjectConverter.INSTANCE.sysUserAddDTOToSysUser(dto);
        String encodedPassword = SecurityUtils.encryptPassword(UserConstants.DEFAULT_PASSWORD);
        sysUser.setUserId(UUID.fastUUID().toString().replaceAll("-", ""));
        sysUser.setPassword(encodedPassword);
        return this.save(sysUser) ? 1 : 0;
    }

    @Override
    public SysUser login(UserLoginDTO dto) {
        // 根据用户名查询用户
        SysUser sysUser = this.getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUserName, dto.getUsername())
                .eq(SysUser::getDelFlag, Constants.NOT_DEL));
        if (ObjectUtil.isNull(sysUser)) {
            throw new ServiceException("用户不存在");
        }
        if (!SecurityUtils.matchesPassword(dto.getPassword(), sysUser.getPassword())) {
            throw new ServiceException("密码错误");
        }
        if (UserConstants.DISABLE.equals(sysUser.getStatus())) {
            throw new ServiceException("用户已停用");
        }
        return sysUser;
    }

    @Override
    public UserLoginVO generateToken(SysUser sysUser) {
        String token = IdUtils.fastUUID();
        sysUser.setToken(token);
        refreshToken(sysUser);

        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(SecurityConstants.USER_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, sysUser.getUserId());
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, sysUser.getUserName());

        // 接口返回信息
        return UserLoginVO.builder().token(JwtUtil.createJWT(jwtProperties.getSecretKey(), jwtProperties.getTtl(), claimsMap))
                .expireTime(expireTime)
                .userId(sysUser.getUserId())
                .username(sysUser.getUserName())
                .nickName(sysUser.getNickName())
                .build();
    }

    /**
     * 刷新令牌有效期
     *
     * @param sysUser 登录信息
     */
    public void refreshToken(SysUser sysUser) {
        sysUser.setLoginTime(System.currentTimeMillis());
        sysUser.setExpireTime(sysUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(sysUser.getToken());
        redisService.setCacheObject(userKey, sysUser, expireTime, TimeUnit.MINUTES);
    }

    private String getTokenKey(String token) {
        return ACCESS_TOKEN + token;
    }
}
